// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package app

import (
	"appengine"
	"appengine/datastore"
	"appengine/urlfetch"
	"fmt"
	"http"
	"goauth2.googlecode.com/hg/oauth"
)

var oauthConfig = &oauth.Config{
	Scope:    "https://www.googleapis.com/auth/devstorage.read_write",
	AuthURL:  "https://accounts.google.com/o/oauth2/auth",
	TokenURL: "https://accounts.google.com/o/oauth2/token",
}

func init() {
	// Set the (dev|prod)(ClientId|ClientSecret|RedirectURL) constants
	// in another file that belongs to this package. This is so that
	// OAuth credentials are not stored in the version control system.
	if appengine.IsDevAppServer() {
		oauthConfig.ClientId = devClientId
		oauthConfig.ClientSecret = devClientSecret
		oauthConfig.RedirectURL = devRedirectURL
	} else {
		oauthConfig.ClientId = prodClientId
		oauthConfig.ClientSecret = prodClientSecret
		oauthConfig.RedirectURL = prodRedirectURL
	}

	http.HandleFunc("/oauth2/setup", oauth2setup)
	http.HandleFunc("/oauth2/callback", oauth2callback)
}

// oauth2setup redirects the admin user to Google's OAuth service,
// so that they may grant access to their Google Cloud Storage buckets.
func oauth2setup(w http.ResponseWriter, r *http.Request) {
	http.Redirect(w, r, oauthConfig.AuthCodeURL("foo"), http.StatusFound)
}

// oauth2callback is the handler to which Google's OAuth service redirects the
// user after they have granted the appropriate permissions.
//
// It makes an out-of-band request to Google to exchange a one-time code for
// an access token and request token. These tokens are stored in the datastore.
func oauth2callback(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)

	// Create an oauth transport with a urlfetch.Transport embedded inside.
	t := &oauth.Transport{
		Config:    oauthConfig,
		Transport: &urlfetch.Transport{Context: c},
	}

	// Exchange the code for access and refresh tokens.
	tok, err := t.Exchange(r.FormValue("code"))
	if err != nil {
		c.Errorf("Exchange(%q): %v", r.FormValue("code"), err)
		http.Error(w, err.String(), http.StatusInternalServerError)
		return
	}

	// Store the tokens in the datastore.
	key := datastore.NewKey(c, "OAuth2Token", "goray", 0, nil)
	if _, err := datastore.Put(c, key, tok); err != nil {
		c.Errorf("Put Token: %v", err)
		http.Error(w, err.String(), http.StatusInternalServerError)
		return
	}

	fmt.Fprint(w, "OK")
}

// oauth2Transport retrieves the OAuth Tokens from the datastore and returns
// an http.RoundTripper that can make authenticated calls.
// Nil is returned if no tokens are found.
func oauth2Transport(c appengine.Context) http.RoundTripper {
	key := datastore.NewKey(c, "OAuth2Token", "goray", 0, nil)
	tok := new(oauth.Token)
	if err := datastore.Get(c, key, tok); err != nil {
		c.Errorf("Get Token: %v", err)
		return nil
	}
	return &oauth.Transport{
		Config:    oauthConfig,
		Token:     tok,
		Transport: &urlfetch.Transport{Context: c},
	}
}
